import {ForbiddenException, Injectable} from '@nestjs/common';
import {Contract} from 'web3-eth-contract';
import {userDto} from '../user/dto/user.dto';
import {userdetail} from 'src/user/dto/userdetail.dto';
import {newproduct} from '../product/dto/newproduct.dto';
import {processinfo} from '../product/dto/processinfo.dto';
import {transport} from 'src/product/dto/transport.dto';
import {collectinfo} from 'src/product/dto/clooectinfo.dto';
// import * as Web3 from 'web3';
//不能改，就是require,import会出错，找不到
const Web3 = require("web3")
// import Index from "./contracts/index.json";
// import User from "./contracts/User.json";
// import Product from "./contracts/product.json";
const Index = require("./contracts/index.json")
const User = require("./contracts/User.json")
const Product = require("./contracts/product.json")

@Injectable()
export class Web3Service {

    private web3: any;
    private indexContract: Contract;
    private indexAddress = "0xd99a5Dbd5d52aE5f18eA4B3b63740725a507520F";
    private accounts: Array<any>;
    private gasNum: string;

    constructor() {
        // console.log("web3", Web3);
        const localhost = "http://127.0.0.1:7545";
        this.gasNum = "5000000";
        const provider: any = new Web3.providers.HttpProvider(localhost);
        this.web3 = new Web3(provider);
        this.loadIndexContract();
    }

    //加载indexContract
    private async loadIndexContract() {
        this.indexContract = await new this.web3.eth.Contract(Index.abi as any, this.indexAddress);
        this.accounts = await this.web3.eth.getAccounts();
    }

    getWeb3(): any {
        return this.web3;
    }

    // indexContract相关方法
    //√
    async getAllUser() {
        const userList = await this.indexContract.methods.getAllUsers().call();
        const allList = [];
        for (const contractAddress of userList) {
            const info: any = await this.getUser(contractAddress);
            info.hashAddress = contractAddress;
            allList.push(info);
        }
        return allList;
    }

    //√
    async pushUser(userAdd: any) {
        await this.indexContract.methods.pushUser(userAdd)
            .send({from: this.accounts[0], gas: this.gasNum});
    }

    //在完成pro合约相关方法后测试√
    async getAllProduct() {
        const proList = await this.indexContract.methods.getAllProducts().call();
        const allList = [];
        for (const contractAddress of proList) {
            const info: any = await this.getProBasic(contractAddress);
            info.hashAddress = contractAddress;
            allList.push(info);
        }
        return allList;
    }

    //√
    async pushProduct(proAdd: any) {
        await this.indexContract.methods.pushProduct(proAdd)
            .send({from: this.accounts[0], gas: this.gasNum});
    }

    //////userContract相关方法
    //创建用户，就是注册
    //√
    async createUser(userDto: userDto) {
        const {name, password, range} = userDto;
        const contract = new this.web3.eth.Contract(User.abi);
        const deploy = contract.deploy({
            data: User.bytecode,
            arguments: [name, password, range]
        });
        const result = await deploy.send({from: this.accounts[0], gas: this.gasNum});
        const newAdd = result.options.address;
        await this.pushUser(newAdd);
        return newAdd;
    }

    //获取user信息
    //√
    async getUser(userAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd);
        const userInfo = await user.methods.getDetail().call();
        const {
            '0': userName, '1': password, '2': range, '3': name,
            '4': idendity, '5': location, '6': mobile, '7': email,
            '8': companyAddress, '9': companyName
        } = userInfo;
        return {
            userName, password, range, name, idendity, location, mobile,
            email, companyAddress, companyName
        }
    }

    //获取用户下的产品列表//√
    async getProFromUser(userAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        const userProList = await user.methods.getProducts().call();
        const allList = [];
        for (const contractAddress of userProList) {
            const info: any = await this.getProBasic(contractAddress);
            info.hashAddress = contractAddress;
            allList.push(info);
        }
        return allList;
    }

    //√
    async pushProToUser(userAdd: string, proAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        await user.methods.pushProduct(proAdd).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //获取用户是否拉黑
    //√
    async getUserEnable(userAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        const isEnable: boolean = await user.methods.isEnable().call();
        return isEnable;
    }

    //√
    async setUserEnable(userAdd: string, isEnable: boolean, message: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        await user.methods.setEnable(isEnable, message).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //√用户被限制权限原因
    async getUserMessage(userAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd);
        return await user.methods.message().call();
    }

    //查询用户是否进一步填写完整信息，只有完整信息才能继续操作
    //√
    async getUserFull(userAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        const isFull: boolean = await user.methods.isFull().call();
        return isFull;
    }

    //实际上，在setDetail方法中就会更新full字段
    //√
    async setUserFull(userAdd: string, setFull: boolean) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        await user.methods.setFull(setFull).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //获取用户权限等级
    //√
    async getUserRange(userAdd: string) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd)
        const range: string = await user.methods.getRange().call();
        //由于精度问题uint类型被转化为string
        return range;
    }

    //√
    async setUserRange(userAdd: string, modifiedAdd: string, range: number) {
        const r: string = await this.getUserRange(userAdd);
        //只有超级管理员有权限修改
        if (r !== "0") {
            throw new ForbiddenException('不是超级管理员');
        }
        if (range < 0 || range > 2) {
            throw new ForbiddenException('权限等级为0-2');
        }
        const mo = await new this.web3.eth.Contract(User.abi as any, modifiedAdd);
        await mo.methods.setRange(range).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //设置用户更多信息
    //√
    async setUserDetail(userAdd: string, userDetail: userdetail) {
        const user = await new this.web3.eth.Contract(User.abi as any, userAdd);
        await user.methods.setDetail(
            userDetail.userName, userDetail.password,
            userDetail.name, userDetail.idendity, userDetail.location,
            userDetail.mobile, userDetail.email, userDetail.companyAddress,
            userDetail.companyName).send({
            from: this.accounts[0],
            gas: this.gasNum
        });

    }


    //////product合约相关方法
    //√
    async createPro(newPro: newproduct) {
        const contract = new this.web3.eth.Contract(Product.abi);
        const deploy = contract.deploy({
            data: Product.bytecode,
            arguments: [
                newPro.proName,
                newPro.description,
                newPro.madeAddress,
                newPro.companyName,
                newPro.madeTime,
                newPro.applicant,
                newPro.owner // 传入合约拥有者的地址
            ]
        });
        const result = await deploy.send({
            from: this.accounts[0],
            gas: this.gasNum
        });
        const newAdd = result.options.address;
        await this.pushProduct(newAdd);
        await this.pushProToUser(newPro.owner, newAdd);
        return newAdd;
    }

    //√
    async getProCollect(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd)
        try {
            const collect = await pro.methods.getCollect().call();
            return collect;
        } catch (err) {
            console.log('-------error:', err);
            throw new ForbiddenException('数据为空');
        }
    }

    /////√
    async getProBasic(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        const basic = await pro.methods.getBasic().call();
        const {
            '0': productName, '1': description, '2': madeAddress,
            '3': companyName, '4': madeTime, '5': applicant, '6': price, '7': approver,
            '8': sellAddress
        } = basic
        return {
            productName, description, madeAddress,
            companyName, madeTime, applicant, price, approver,
            sellAddress
        };
    }

    //√
    async getProProcess(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd)
        try {
            const processList = await pro.methods.getProcess().call();
            return processList;
        } catch (err) {
            console.log('-------error:', err);
            throw new ForbiddenException('数据为空');
        }
    }

    //√
    async getProTransport(proAdd: string) {
        try {
            const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd)
            const transList = await pro.methods.getTransport().call();
            return transList;
        } catch (err) {
            console.log('-------error:', err);
            throw new ForbiddenException('数据为空');
        }
    }

    //√
    async getIfApprove(proAdd: string) {
        try {
            const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd)
            const approved = await pro.methods.isApprove().call();
            const approver = await pro.methods.approver().call();
            const appMessage = await pro.methods.appMessage().call();
            return {approved, approver, appMessage};
        } catch (err) {
            console.log('-------error:', err);
            throw new ForbiddenException('数据为空');
        }
    }

    //价格的格式是500/袋（500克）
    //√
    async applyApprove(proAdd: string, certificate: string, price: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.setApplyInfo(certificate, price).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }


    //3.31开始
    //重新审批√
    async applyAgain(proAdd: string, certificate: string, price: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.applyAgain(certificate, price).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //isApprove:0,1,2=>未审批，合格，不合格
    //√
    async setIfApprove(proAdd: string, approver: string,
                       isApprove: number, appMessage: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.setApprove(approver, isApprove, appMessage).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }


    async pushProcessToPro(proAdd: string, processinfo: processinfo) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.pushProcess(processinfo.time, processinfo.temperature,
            processinfo.weather, processinfo.operator,
            processinfo.imageId, processinfo.message).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //√
    async pushTransportToPro(proAdd: string, trans: transport) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.pushTransport(trans.time, trans.transportAddress,
            trans.operator, trans.carLicense).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //√
    async setProCollect(proAdd: string, collectinfo: collectinfo) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.setCollect(collectinfo.time,
            collectinfo.collectAddress,
            collectinfo.operator, collectinfo.imageId,
            collectinfo.message).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //√√√
    async setIsDeleted(proAdd: string, isDeleted: boolean) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.setIsDeleted(isDeleted).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //√
    async getIsDeleted(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        const isdeleted: boolean = await pro.methods.isDeleted().call();
        return isdeleted;
    }

    //√
    async setSellAddress(proAdd: string, sellAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        await pro.methods.setSellAddress(sellAdd).send({
            from: this.accounts[0],
            gas: this.gasNum
        });
    }

    //√
    async getSellAddress(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        const sellAdd = await pro.methods.sellAddress().call();
        return sellAdd;
    }

    /**√√√
     * 获得产品审批建议
     * @param proAdd 产品哈希地址
     */
    async getAppMessage(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        return await pro.methods.appMessage().call();
    }

    async getCertificate(proAdd: string) {
        const pro = await new this.web3.eth.Contract(Product.abi as any, proAdd);
        return await pro.methods.certificate().call();
    }
}
